package com.J.Plane;

import javax.microedition.khronos.opengles.GL10;

/**
 * This guy controls camera which can be local or global. 
 */
public class Camera extends Frustum {
	
	/**
	 * These are camera attributes.
	 * Global camera or local camera
	 */
	public static final int 	GLOBAL_CAMERA	=	1;
	public static final int 	LOCAL_CAMERA	=	2;
	
	private Vector3 mEye;
	private Vector3 mAt;
	private Vector3 mUp;
	
	private boolean mIsGlobal = false;
	
	private boolean mNeedUpdateView;
	private Matrix44 mCachedViewMatrix;
	
	
	/**
	 * Constructor
	 * @param cameraAttr
	 */
	public Camera(int cameraAttr) {
		mEye = new Vector3(0,0,-500);
		mAt = new Vector3(0,0,0);
		mUp = new Vector3(0,1,0);
		
		mCachedViewMatrix = new Matrix44();
		mNeedUpdateView = true;
		
		// Mark camera as global camera.
		mIsGlobal = cameraAttr == GLOBAL_CAMERA;
	}
	
	
	public Camera eye(float x, float y, float z) {
		boolean isSame = mEye.isSame(x,y,z);
		if(isSame)
			return this;
		
		mEye.set(x, y, z);
		needUpdateView();
		return this;
	}
	public final Vector3 eye() {
		return mEye;
	}
	public Camera at(float x, float y, float z) {
		boolean isSame = mAt.isSame(x,y,z);
		if(isSame)
			return this;
		
		mAt.set(x, y, z);
		needUpdateView();
		return this;
	}
	public final Vector3 at() {
		return mAt;
	}
	public Camera up(float x, float y, float z) {
		boolean isSame = mUp.isSame(x,y,z);
		if(isSame)
			return this;
		
		mUp.set(x, y, z);
		needUpdateView();
		return this;
	}
	public Vector3 up() {
		return mUp;
	}
	

	private void needUpdateView() {
		mNeedUpdateView = true;
	}
	
	
	
	/**
	 * Set up view-to-world matrix.
	 * @param [in]gl	OpenGL context
	 */
	public void setViewToWorldMatrix(GL10 gl) {
		if(mNeedUpdateView) {
			updateViewMatrix();
		}
		
		gl.glMatrixMode(GL10.GL_MODELVIEW);
		if(mIsGlobal)
			gl.glLoadIdentity();
		
		gl.glMultMatrixf(mCachedViewMatrix.getFloatBuffer());
	}
	
	
	/**
	 * Update view matrix and cache it.
	 * 
	 */
	private void updateViewMatrix() {
		mNeedUpdateView = false;
		
		Vector3 dir = new Vector3();
		Vector3.subtract(mAt, mEye, dir);
		dir.normalize();

		Vector3 up = new Vector3(mUp);
		Vector3 side = new Vector3();
		Vector3.cross(dir, up, side);
		side.normalize();
		
		// Recalc up vector for correct direction.
		Vector3.cross(side, dir, up);
		
		Matrix44 m = mCachedViewMatrix;
		m.n00 = side.x;
		m.n10 = side.y;
		m.n20 = side.z;
		
		m.n01 = up.x;
		m.n11 = up.y;
		m.n21 = up.z;
		
		m.n02 = -dir.x;
		m.n12 = -dir.y;
		m.n22 = -dir.z;
		
		m.n03 = m.n13 = m.n23 = 
		m.n30 = m.n31 = m.n32 = 0;
		m.n33 = 1;
		
		m.preTranslation(-mEye.x, -mEye.y, -mEye.z);
	}
}


